/*
* The shader code below is licensed under the Creative Commons Attribution-Share Alike 3.0 
* license.
*
* You are free:
*	To Share: to copy, distribute, display, and perform the work
*	To Remix: to make derivative works 
*
*	Under the following conditions:
*		* Attribution - You must attribute the work in the manner specified by the author or
*			licensor (but not in any way that suggests that they endorse you or your use of the work).
*		* Share Alike - If you alter, transform, or build upon this work, you may distribute
*			the resulting work only under the same, similar or a compatible license.
*
*		For any reuse or distribution, you must make clear to others the license terms of this work.
*		The best way to do this is with a link to the above web page (http;//www.tesla-engine.net)
*
*		Any of the above conditions can be waived if you get permission from the copyright holder.
*
*		Apart from the remix rights granted under this license, nothing in this license impairs or
*		restricts the author's moral rights.
*/

#include "Structures.fxh"
#include "Lighting.fxh"

//World variables
uniform float4x4 WVP; //WorldViewProjection
uniform float4x4 World;
uniform float4x4 WorldIT; //WorldInverseTranspose
uniform float3 EyePos;

//Lighting variables
uniform float3 GlobalAmbient : GLOBALAMBIENT = { 0.0f, 0.0f, 0.0f };
uniform Light Lights[4] : LIGHTLIST;
uniform int NumLights : LIGHTCOUNT = 0;


//Material properties
uniform float3 MatDiffuse = { 1.0f, 1.0f, 1.0f};
uniform float3 MatAmbient = { 0.2f, 0.2f, 0.2f};
uniform float3 MatEmissive = { 0.0f, 0.0f, 0.0f};
uniform float3 MatSpecular = { 0.0f, 0.0f, 0.0f};
uniform float MatShininess = 16.0f;
uniform float Alpha = 1.0f;

uniform texture2D DiffuseMap;
uniform sampler2D DiffuseMapSampler = 
sampler_state {
  Texture = <DiffuseMap>;
  MipFilter = LINEAR;
  MinFilter = LINEAR;
  MagFilter = LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

//
// **** LitBasicColor ****
//

VSOutputNm VS_LitBasicNm(VSInputNm input) {
	float4 pos = float4(input.Position, 1.0);

	VSOutputNm output;
	output.PositionPS = mul(pos, WVP);
	output.PositionWS = mul(pos, World).xyz;
	output.Normal = normalize(mul(float4(input.Normal, 0.0), WorldIT).xyz);
	return output;
}

float4 PS_LitBasicNm(VSOutputNm input) : SV_TARGET {
	//Individual light contribs from each light
	float3 lightContribs = float3(0,0,0);

	//Calculate the V vector for specular highlights
	float3 V = normalize(EyePos - input.PositionWS);

	float3 normal = input.Normal;
	float3 pos = input.PositionWS;

	Material mat;
	mat.Ambient = MatAmbient;
	mat.Diffuse = MatDiffuse;
	mat.Specular = MatSpecular;
	mat.Shininess = MatShininess;

	for(int i = 0; i < NumLights; i++) {
		Light l = Lights[i];
		LightResult result = ComputeSingleLight(l, pos, normal, V, mat);
		float3 diffuse = result.Diffuse + result.Ambient;
		float3 specular = result.Specular;
		lightContribs += ComputeAttenuation(pos, l) * SpotEffect(pos, l) * (diffuse + specular);
	}

	float3 color = (MatAmbient * GlobalAmbient) + MatEmissive + lightContribs;

	return float4(color, Alpha);
}

technique LitBasicColor {
	pass Pass0 {
    VertexShader = compile vs_3_0 VS_LitBasicNm();
    PixelShader = compile ps_3_0 PS_LitBasicNm();
	}
}


//
// **** LitBasicVertColor ****
//

VSOutputNmVc VS_LitBasicNmVc(VSInputNmVc input) {
	float4 pos = float4(input.Position, 1.0);

	VSOutputNmVc output;
	output.PositionPS = mul(pos, WVP);
	output.PositionWS = mul(pos, World).xyz;
	output.Normal = normalize(mul(float4(input.Normal, 0.0), WorldIT).xyz);
	output.VertColor = input.VertColor;
	return output;
}

float4 PS_LitBasicNmVc(VSOutputNmVc input) : SV_TARGET {
	//Individual light contribs from each light
	float3 lightContribs = float3(0,0,0);

	//Calculate the V vector for specular highlights
	float3 V = normalize(EyePos - input.PositionWS);

	Material mat;
	mat.Ambient = MatAmbient;
	mat.Diffuse = MatDiffuse;
	mat.Specular = MatSpecular;
	mat.Shininess = MatShininess;

	
	float3 normal = input.Normal;
	float3 pos = input.PositionWS;

	for(int i = 0; i < NumLights; i++) {
		Light l = Lights[i];
		LightResult result = ComputeSingleLight(l, pos, normal, V, mat);
		float3 diffuse = (result.Diffuse + result.Ambient) * input.VertColor.rgb;
		float3 specular = result.Specular;
		lightContribs += ComputeAttenuation(pos, l) * SpotEffect(pos, l) * (diffuse + specular);
	}

	float3 color = (MatAmbient * GlobalAmbient) + MatEmissive + lightContribs;


	return float4(color, Alpha * input.VertColor.a);
}

technique LitBasicVertColor {
	pass Pass0 {
    VertexShader = compile vs_3_0 VS_LitBasicNmVc();
    PixelShader = compile ps_3_0 PS_LitBasicNmVc();
	}
}


//
// **** LitBasicTexture ****
//

VSOutputNmTx VS_LitBasicNmTx(VSInputNmTx input) {
	float4 pos = float4(input.Position, 1.0);

	VSOutputNmTx output;
	output.PositionPS = mul(pos, WVP);
	output.PositionWS = mul(pos, World).xyz;
	output.Normal = normalize(mul(float4(input.Normal, 0.0), WorldIT).xyz);
	output.TexCoord = input.TexCoord;
	return output;
}

float4 PS_LitBasicNmTx(VSOutputNmTx input) : SV_TARGET {
	//Individual light contribs from each light
	float3 lightContribs = float3(0,0,0);

	//Get the diffuse map color
	float4 albedo = tex2D(DiffuseMapSampler, input.TexCoord);

	Material mat;
	mat.Ambient = MatAmbient;
	mat.Diffuse = MatDiffuse;
	mat.Specular = MatSpecular;
	mat.Shininess = MatShininess;

	//Calculate the V vector for specular highlights
	float3 V = normalize(EyePos - input.PositionWS);

	float3 normal = input.Normal;
	float3 pos = input.PositionWS;

	for(int i = 0; i < NumLights; i++) {
		Light l = Lights[i];
		LightResult result = ComputeSingleLight(l, pos, normal, V, mat);
		float3 diffuse = (result.Diffuse + result.Ambient) * albedo.rgb;
		float3 specular = result.Specular;
		lightContribs += ComputeAttenuation(pos, l) * SpotEffect(pos, l) * (diffuse + specular);
	}

	float3 color = (MatAmbient * GlobalAmbient) + MatEmissive + lightContribs;

	return float4(color, Alpha * albedo.a);
}

technique LitBasicTexture {
	pass Pass0 {
    VertexShader = compile vs_3_0 VS_LitBasicNmTx();
    PixelShader = compile ps_3_0 PS_LitBasicNmTx();
	}
}

//
// **** LitBasicVertColorTexture ****
//

VSOutputNmVcTx VS_LitBasicNmVcTx(VSInputNmVcTx input) {
	float4 pos = float4(input.Position, 1.0);

	VSOutputNmVcTx output;
	output.PositionPS = mul(pos, WVP);
	output.PositionWS = mul(pos, World).xyz;
	output.Normal = normalize(mul(float4(input.Normal, 0.0), WorldIT).xyz);
	output.VertColor = input.VertColor;
	output.TexCoord = input.TexCoord;
	return output;
}

float4 PS_LitBasicNmVcTx(VSOutputNmVcTx input) : SV_TARGET {
	//Individual light contribs from each light
	float3 lightContribs = float3(0,0,0);

	//Get the diffuse map color
	float4 albedo = tex2D(DiffuseMapSampler, input.TexCoord);

	Material mat;
	mat.Ambient = MatAmbient;
	mat.Diffuse = MatDiffuse;
	mat.Specular = MatSpecular;
	mat.Shininess = MatShininess;

	//Calculate the V vector for specular highlights
	float3 V = normalize(EyePos - input.PositionWS);

	float3 normal = input.Normal;
	float3 pos = input.PositionWS;

	for(int i = 0; i < NumLights; i++) {
		Light l = Lights[i];
		LightResult result = ComputeSingleLight(l, pos, normal, V, mat);
		float3 diffuse = (result.Diffuse + result.Ambient) * input.VertColor.rgb * albedo.rgb;
		float3 specular = result.Specular;
		lightContribs += ComputeAttenuation(pos, l) * SpotEffect(pos, l) * (diffuse + specular);
	}

	float3 color = (MatAmbient * GlobalAmbient) + MatEmissive + lightContribs;

	return float4(color, Alpha * input.VertColor.a * albedo.a);
}

technique LitBasicVertColorTexture {
	pass Pass0 {
    VertexShader = compile vs_3_0 VS_LitBasicNmVcTx();
    PixelShader = compile ps_3_0 PS_LitBasicNmVcTx();
	}
}
